using System;
using System.Diagnostics;
using System.IO;
using System.Configuration;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.Generic;

namespace WebDavSyncTool
{
    public class RCloneManager : IDisposable
    {
        public enum SyncDirection
        {
            UploadOnly,      // 仅上传（客户端到服务端）
            DownloadOnly,    // 仅下载（服务端到客户端）
            Bidirectional    // 双向同步
        }

        private readonly string rclonePath;
        private readonly string configPath;
        private readonly string webDavRemoteName;
        private readonly string sambaRemoteName;
        private readonly string sourceDirectory;
        private readonly string webDavTargetDirectory;
        private readonly string sambaTargetDirectory;
        private readonly RCloneConfig rcloneConfig;
        private readonly SyncDirection syncDirection;
        private readonly bool enableWebDav;
        private readonly bool enableSamba;
        private Process currentProcess;  // 添加字段跟踪当前进程
        private bool isDisposed;

        public class RCloneConfig
        {
            private int retries;
            private int transfers;
            private int bwLimit;
            private string fileFilter;
            private int minAge;
            private int maxAge;
            private int minSize;
            private int maxSize;

            public RCloneConfig()
            {
                retries = 3;
                transfers = 4;
                bwLimit = 0;
                fileFilter = "";
                minAge = 0;
                maxAge = 0;
                minSize = 0;
                maxSize = 0;
            }

            public int Retries 
            { 
                get { return retries; }
                set { retries = value; }
            }

            public int Transfers
            {
                get { return transfers; }
                set { transfers = value; }
            }

            public int BwLimit
            {
                get { return bwLimit; }
                set { bwLimit = value; }
            }

            public string FileFilter
            {
                get { return fileFilter; }
                set { fileFilter = value; }
            }

            public int MinAge
            {
                get { return minAge; }
                set { minAge = value; }
            }

            public int MaxAge
            {
                get { return maxAge; }
                set { maxAge = value; }
            }

            public int MinSize
            {
                get { return minSize; }
                set { minSize = value; }
            }

            public int MaxSize
            {
                get { return maxSize; }
                set { maxSize = value; }
            }

            public static RCloneConfig FromAppSettings()
            {
                var config = new RCloneConfig();
                
                string value;
                int intValue;

                value = ConfigurationManager.AppSettings["RCloneRetries"];
                if (value != null && int.TryParse(value, out intValue))
                {
                    config.Retries = intValue;
                }

                value = ConfigurationManager.AppSettings["RCloneTransfers"];
                if (value != null && int.TryParse(value, out intValue))
                {
                    config.Transfers = intValue;
                }

                value = ConfigurationManager.AppSettings["RCloneBwLimit"];
                if (value != null && int.TryParse(value, out intValue))
                {
                    config.BwLimit = intValue;
                }

                config.FileFilter = ConfigurationManager.AppSettings["RCloneFileFilter"] ?? "";

                value = ConfigurationManager.AppSettings["RCloneMinAge"];
                if (value != null && int.TryParse(value, out intValue))
                {
                    config.MinAge = intValue;
                }

                value = ConfigurationManager.AppSettings["RCloneMaxAge"];
                if (value != null && int.TryParse(value, out intValue))
                {
                    config.MaxAge = intValue;
                }

                value = ConfigurationManager.AppSettings["RCloneMinSize"];
                if (value != null && int.TryParse(value, out intValue))
                {
                    config.MinSize = intValue;
                }

                value = ConfigurationManager.AppSettings["RCloneMaxSize"];
                if (value != null && int.TryParse(value, out intValue))
                {
                    config.MaxSize = intValue;
                }

                return config;
            }
        }

        public RCloneManager()
        {
            try
            {
                rclonePath = GetRclonePath();
                configPath = Path.Combine(Path.GetDirectoryName(rclonePath), "rclone.conf");
                
                Logger.LogInfo(string.Format("RClone路径: {0}", rclonePath));
                Logger.LogInfo(string.Format("配置文件路径: {0}", configPath));
                
                webDavRemoteName = ConfigurationManager.AppSettings["WebDavRemoteName"] ?? "webdav";
                sambaRemoteName = ConfigurationManager.AppSettings["SambaRemoteName"] ?? "samba";
                sourceDirectory = ConfigurationManager.AppSettings["SourceDirectory"];
                webDavTargetDirectory = ConfigurationManager.AppSettings["WebDavTargetDirectory"];
                sambaTargetDirectory = ConfigurationManager.AppSettings["SambaTargetDirectory"];
                
                Logger.LogInfo(string.Format("WebDAV远程名称: {0}", webDavRemoteName));
                Logger.LogInfo(string.Format("Samba远程名称: {0}", sambaRemoteName));
                Logger.LogInfo(string.Format("源目录: {0}", sourceDirectory));

                if (!File.Exists(rclonePath))
                {
                    Logger.LogError(string.Format("未找到rclone.exe: {0}", rclonePath));
                    throw new FileNotFoundException("未找到rclone.exe", rclonePath);
                }

                rcloneConfig = RCloneConfig.FromAppSettings();
                Logger.LogInfo("RClone配置已加载");

                // 读取同步配置
                SyncDirection direction;
                if (Enum.TryParse(ConfigurationManager.AppSettings["SyncDirection"], out direction))
                {
                    syncDirection = direction;
                }
                else
                {
                    syncDirection = SyncDirection.UploadOnly;
                }
                
                enableWebDav = bool.Parse(ConfigurationManager.AppSettings["EnableWebDav"] ?? "true");
                enableSamba = bool.Parse(ConfigurationManager.AppSettings["EnableSamba"] ?? "false");

                EnsureRCloneConfig();
            }
            catch (Exception ex)
            {
                Logger.LogError("RCloneManager初始化失败", ex);
                throw;
            }
        }

        private string GetRclonePath()
        {
            string rclonePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "rclone");
            string rcloneExe;
            
            // 检查系统架构和进程架构
            if (Environment.Is64BitOperatingSystem)
            {
                if (Environment.Is64BitProcess)
                {
                    // 64位进程在64位系统上，使用64位版本
                    rcloneExe = Path.Combine(rclonePath, "rclone.exe");
                    Logger.LogInfo("使用64位版本rclone (64位系统/64位进程)");
                }
                else
                {
                    // 32位进程在64位系统上，使用32位版本
                    rcloneExe = Path.Combine(rclonePath, "x86", "rclone.exe");
                    Logger.LogInfo("使用32位版本rclone (64位系统/32位进程)");
                }
            }
            else
            {
                // 32位系统只能使用32位版本
                rcloneExe = Path.Combine(rclonePath, "x86", "rclone.exe");
                Logger.LogInfo("使用32位版本rclone (32位系统)");
            }

            if (!File.Exists(rcloneExe))
            {
                var error = string.Format("未找到对应的rclone版本: {0}", rcloneExe);
                Logger.LogError(error);
                throw new FileNotFoundException(error);
            }

            return rcloneExe;
        }

        private void EnsureRCloneConfig()
        {
            try
            {
                // 确保配置文件目录存在
                var configDir = Path.GetDirectoryName(configPath);
                if (!Directory.Exists(configDir))
                {
                    Directory.CreateDirectory(configDir);
                }

                // 如果配置文件不存在，创建新的配置
                var config = new System.Text.StringBuilder();
                bool needsUpdate = !File.Exists(configPath);

                // WebDAV配置
                if (enableWebDav)
                {
                    var webDavUrl = ConfigurationManager.AppSettings["WebDavUrl"];
                    var webDavUsername = ConfigurationManager.AppSettings["WebDavUsername"];
                    var webDavPassword = ConfigurationManager.AppSettings["WebDavPassword"];
                    var webDavPasswordIsEncrypted = bool.Parse(ConfigurationManager.AppSettings["WebDavPasswordIsEncrypted"] ?? "false");

                    if (webDavPasswordIsEncrypted && webDavPassword.StartsWith("ENCRYPTED:"))
                    {
                        webDavPassword = webDavPassword.Substring(10);
                    }

                    config.AppendFormat("[{0}]\n", webDavRemoteName);
                    config.AppendLine("type = webdav");
                    config.AppendFormat("url = {0}\n", webDavUrl);
                    config.AppendLine("vendor = other");
                    config.AppendFormat("user = {0}\n", webDavUsername);
                    config.AppendFormat("pass = {0}\n\n", webDavPassword);
                }

                // Samba配置
                if (enableSamba)
                {
                    var sambaHost = ConfigurationManager.AppSettings["SambaHost"];
                    var sambaShare = ConfigurationManager.AppSettings["SambaShare"];
                    var sambaUsername = ConfigurationManager.AppSettings["SambaUsername"];
                    var sambaPassword = ConfigurationManager.AppSettings["SambaPassword"];

                    if (sambaPassword.StartsWith("ENCRYPTED:"))
                    {
                        sambaPassword = sambaPassword.Substring(10);
                    }

                    config.AppendFormat("[{0}]\n", sambaRemoteName);
                    config.AppendLine("type = smb");
                    config.AppendFormat("host = {0}\n", sambaHost);
                    config.AppendFormat("share = {0}\n", sambaShare);
                    config.AppendFormat("user = {0}\n", sambaUsername);
                    config.AppendFormat("pass = {0}\n\n", sambaPassword);
                }

                // 如果配置文件不存在或内容有变化，则写入新配置
                if (needsUpdate || !File.Exists(configPath) || File.ReadAllText(configPath) != config.ToString())
                {
                    File.WriteAllText(configPath, config.ToString(), System.Text.Encoding.UTF8);
                    Logger.LogInfo(string.Format("RClone配置文件已更新: {0}", configPath));
                }

                // 测试连接
                if (enableWebDav)
                {
                    TestConnection(webDavRemoteName, "WebDAV");
                }
                if (enableSamba)
                {
                    TestConnection(sambaRemoteName, "Samba");
                }
            }
            catch (Exception ex)
            {
                Logger.LogError("创建或测试RClone配置文件失败", ex);
                throw;
            }
        }

        private string EncryptPassword(string password)
        {
            // 如果密码已经加密，直接返回
            if (password.StartsWith("ENCRYPTED:"))
            {
                return password.Substring(10);
            }

            using (var process = new Process())
            {
                process.StartInfo = new ProcessStartInfo
                {
                    FileName = rclonePath,
                    Arguments = string.Format("obscure {0}", password),
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true
                };

                process.Start();
                var encryptedPassword = process.StandardOutput.ReadToEnd().Trim();
                process.WaitForExit();

                if (process.ExitCode != 0)
                {
                    throw new Exception("Failed to encrypt password");
                }

                return encryptedPassword;
            }
        }

        private void TestConnection(string remoteName, string type)
        {
            using (var process = new Process())
            {
                process.StartInfo = new ProcessStartInfo
                {
                    FileName = rclonePath,
                    Arguments = string.Format("lsd \"{0}:\" --config \"{1}\"", remoteName, configPath),
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true
                };

                Logger.LogInfo(string.Format("测试{0}连接...", type));
                process.Start();
                var output = process.StandardOutput.ReadToEnd();
                var error = process.StandardError.ReadToEnd();
                process.WaitForExit();

                if (process.ExitCode == 0)
                {
                    Logger.LogInfo(string.Format("{0}连接测试成功", type));
                    Logger.LogInfo("目录列表：\n" + output);
                }
                else
                {
                    Logger.LogError(string.Format("{0}连接测试失败: {1}", type, error));
                }
            }
        }

        private string BuildRCloneArguments(string remoteName, string targetDirectory, SyncDirection direction)
        {
            try
            {
                string command = "sync";  // 始终使用 sync 命令
                string source;
                string dest;

                switch (direction)
                {
                    case SyncDirection.DownloadOnly:
                        source = string.Format("\"{0}:{1}\"", remoteName, targetDirectory);
                        dest = string.Format("\"{0}\"", sourceDirectory);
                        break;
                    case SyncDirection.UploadOnly:
                    default:
                        source = string.Format("\"{0}\"", sourceDirectory);
                        dest = string.Format("\"{0}:{1}\"", remoteName, targetDirectory);
                        break;
                }

                var args = string.Format("{0} {1} {2} --config \"{3}\"", 
                    command, source, dest, configPath);

                // 添加基本参数
                args += string.Format(" --transfers {0}", rcloneConfig.Transfers);
                args += string.Format(" --retries {0}", rcloneConfig.Retries);
                args += " --verbose";
                args += " --progress";
                args += " --ignore-existing"; // 防止覆盖已存在的文件
                args += " --no-update-modtime"; // 不更新修改时间
                args += " --create-empty-src-dirs"; // 创建空源目录
                args += " --copy-links"; // 复制符号链接
                args += " --no-traverse"; // 不遍历目标目录，提高性能

                // 使用 --filter 替代 --include 和 --exclude
                if (!string.IsNullOrEmpty(rcloneConfig.FileFilter))
                {
                    var patterns = rcloneConfig.FileFilter.Split(';');
                    foreach (var pattern in patterns)
                    {
                        args += string.Format(" --filter \"+ {0}\"", pattern.Trim());
                    }
                    args += " --filter \"- *\""; // 排除其他所有文件
                }

                // 添加文件年龄过滤
                if (rcloneConfig.MinAge > 0)
                {
                    args += string.Format(" --min-age {0}m", rcloneConfig.MinAge);
                }
                if (rcloneConfig.MaxAge > 0)
                {
                    args += string.Format(" --max-age {0}m", rcloneConfig.MaxAge);
                }

                // 添加文件大小过滤
                if (rcloneConfig.MinSize > 0)
                {
                    args += string.Format(" --min-size {0}k", rcloneConfig.MinSize);
                }
                if (rcloneConfig.MaxSize > 0)
                {
                    args += string.Format(" --max-size {0}k", rcloneConfig.MaxSize);
                }

                Logger.LogInfo(string.Format("完整的RClone命令: {0} {1}", rclonePath, args));
                return args;
            }
            catch (Exception ex)
            {
                Logger.LogError("构建RClone参数失败", ex);
                throw;
            }
        }

        private string BuildCopyArguments(string source, string dest)
        {
            var args = string.Format("copy {0} {1} --config \"{2}\"",
                source, dest, configPath);

            // 添加基本参数
            args += string.Format(" --transfers {0}", rcloneConfig.Transfers);
            args += string.Format(" --retries {0}", rcloneConfig.Retries);
            args += " --verbose";
            args += " --progress";
            args += " --create-empty-src-dirs"; // 创建空源目录
            args += " --copy-links"; // 复制符号链接
            args += " --no-traverse"; // 不遍历目标目录，提高性能

            return args;
        }

        public async Task SyncAsync()
        {
            try
            {
                // WebDAV同步
                if (enableWebDav)
                {
                    await SyncToRemoteAsync(webDavRemoteName, webDavTargetDirectory, "WebDAV");
                }

                // Samba同步
                if (enableSamba)
                {
                    await SyncToRemoteAsync(sambaRemoteName, sambaTargetDirectory, "Samba");
                }
            }
            catch (Exception ex)
            {
                Logger.LogError("同步过程中发生错误", ex);
                throw;
            }
        }

        private async Task CompareDirectoriesAsync(string remoteName, string targetDirectory, string type)
        {
            try
            {
                Logger.LogInfo(string.Format("开始比对{0}文件差异...", type));
                var args = string.Format("check \"{0}\" \"{1}:{2}\" --config \"{3}\" --one-way",
                    sourceDirectory, remoteName, targetDirectory, configPath);

                using (var process = new Process())
                {
                    process.StartInfo = new ProcessStartInfo
                    {
                        FileName = rclonePath,
                        Arguments = args,
                        UseShellExecute = false,
                        RedirectStandardOutput = true,
                        RedirectStandardError = true,
                        CreateNoWindow = true,
                        StandardOutputEncoding = System.Text.Encoding.UTF8,
                        StandardErrorEncoding = System.Text.Encoding.UTF8
                    };

                    process.Start();
                    var output = await process.StandardOutput.ReadToEndAsync();
                    var error = await process.StandardError.ReadToEndAsync();
                    await Task.Run(() => process.WaitForExit());

                    if (process.ExitCode == 0)
                    {
                        Logger.LogInfo(string.Format("{0}文件比对完成，两边文件一致", type));
                    }
                    else
                    {
                        Logger.LogError(string.Format("{0}文件比对发现差异: {1}", type, error));
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(string.Format("{0}文件比对过程中发生错误", type), ex);
                throw;
            }
        }

        private class FileChangeResult
        {
            public List<string> LocalNew { get; set; }
            public List<string> RemoteNew { get; set; }
        }

        private async Task<FileChangeResult> GetFileChangesAsync(string remoteName, string targetDirectory)
        {
            var localNew = new List<string>();
            var remoteNew = new List<string>();

            // 获取本地文件列表
            var localArgs = string.Format("lsf \"{0}\" --config \"{1}\" --recursive",
                sourceDirectory, configPath);
            
            // 获取远程文件列表
            var remoteArgs = string.Format("lsf \"{0}:{1}\" --config \"{2}\" --recursive",
                remoteName, targetDirectory, configPath);

            try
            {
                var localFiles = await GetFileListAsync(localArgs);
                var remoteFiles = await GetFileListAsync(remoteArgs);

                // 比较文件列表
                localNew = localFiles.Except(remoteFiles).ToList();
                remoteNew = remoteFiles.Except(localFiles).ToList();

                return new FileChangeResult 
                { 
                    LocalNew = localNew, 
                    RemoteNew = remoteNew 
                };
            }
            catch (Exception ex)
            {
                Logger.LogError("获取文件变更列表失败", ex);
                throw;
            }
        }

        private async Task<List<string>> GetFileListAsync(string arguments)
        {
            var files = new List<string>();
            using (var process = new Process())
            {
                process.StartInfo = new ProcessStartInfo
                {
                    FileName = rclonePath,
                    Arguments = arguments,
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true,
                    StandardOutputEncoding = System.Text.Encoding.UTF8
                };

                process.Start();
                while (!process.StandardOutput.EndOfStream)
                {
                    var line = await process.StandardOutput.ReadLineAsync();
                    if (!string.IsNullOrEmpty(line))
                    {
                        files.Add(line.Trim());
                    }
                }
                await Task.Run(() => process.WaitForExit());
            }
            return files;
        }

        private async Task SyncToRemoteAsync(string remoteName, string targetDirectory, string type)
        {
            try
            {
                if (syncDirection == SyncDirection.Bidirectional)
                {
                    // 先比对文件差异
                    var changes = await GetFileChangesAsync(remoteName, targetDirectory);
                    
                    // 记录变更信息
                    if (changes.LocalNew.Any())
                    {
                        Logger.LogInfo(string.Format("本地新增文件: {0}", string.Join(", ", changes.LocalNew)));
                    }
                    if (changes.RemoteNew.Any())
                    {
                        Logger.LogInfo(string.Format("远程新增文件: {0}", string.Join(", ", changes.RemoteNew)));
                    }

                    // 如果本地有新文件，先复制到远程
                    if (changes.LocalNew.Any())
                    {
                        Logger.LogInfo(string.Format("开始复制新增文件到{0}", type));
                        var copyArgs = BuildCopyArguments(
                            string.Format("\"{0}\"", sourceDirectory),
                            string.Format("\"{0}:{1}\"", remoteName, targetDirectory)
                        );
                        await RunRcloneProcessAsync(copyArgs, type + "复制上传");
                    }

                    // 如果远程有新文件，复制到本地
                    if (changes.RemoteNew.Any())
                    {
                        Logger.LogInfo(string.Format("开始从{0}复制新增文件", type));
                        var copyArgs = BuildCopyArguments(
                            string.Format("\"{0}:{1}\"", remoteName, targetDirectory),
                            string.Format("\"{0}\"", sourceDirectory)
                        );
                        await RunRcloneProcessAsync(copyArgs, type + "复制下载");
                    }

                    // 执行完整同步
                    Logger.LogInfo(string.Format("执行{0}完整同步", type));
                    var syncArgs = BuildRCloneArguments(remoteName, targetDirectory, syncDirection);
                    await RunRcloneProcessAsync(syncArgs, type + "同步");

                    // 最后进行完整性校验
                    await CompareDirectoriesAsync(remoteName, targetDirectory, type);
                }
                else
                {
                    var arguments = BuildRCloneArguments(remoteName, targetDirectory, syncDirection);
                    await RunRcloneProcessAsync(arguments, type);
                    
                    // 单向同步后也进行完整性校验
                    await CompareDirectoriesAsync(remoteName, targetDirectory, type);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(string.Format("{0}同步过程中发生错误", type), ex);
                throw;
            }
        }

        private async Task RunRcloneProcessAsync(string arguments, string type)
        {
            using (var process = new Process())
            {
                process.StartInfo = new ProcessStartInfo
                {
                    FileName = rclonePath,
                    Arguments = arguments,
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true,
                    StandardOutputEncoding = System.Text.Encoding.UTF8,
                    StandardErrorEncoding = System.Text.Encoding.UTF8
                };

                currentProcess = process;

                currentProcess.OutputDataReceived += (sender, e) =>
                {
                    if (!string.IsNullOrEmpty(e.Data))
                    {
                        try
                        {
                            // 检查是否包含传输信息
                            if (e.Data.Contains("Transferred:") || 
                                e.Data.Contains("Elapsed time:") ||
                                e.Data.Contains("INFO"))
                            {
                                Logger.LogInfo(string.Format("RClone: {0}", e.Data));
                            }
                            else
                            {
                                Logger.LogInfo(string.Format("RClone输出: {0}", e.Data));
                            }
                        }
                        catch (Exception ex)
                        {
                            Logger.LogError("处理RClone输出时发生错误", ex);
                        }
                    }
                };

                currentProcess.ErrorDataReceived += (sender, e) =>
                {
                    if (!string.IsNullOrEmpty(e.Data))
                    {
                        // 只有真正的错误才记录为错误
                        if (e.Data.Contains("ERROR") && 
                            !e.Data.Contains("Using --filter") && // 忽略filter警告
                            !e.Data.Contains("Transferred:") &&
                            !e.Data.Contains("Elapsed time:"))
                        {
                            Logger.LogError(string.Format("RClone错误: {0}", e.Data));
                        }
                        else
                        {
                            Logger.LogInfo(string.Format("RClone: {0}", e.Data));
                        }
                    }
                };

                Logger.LogInfo(string.Format("正在启动{0} RClone进程...", type));
                process.Start();
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();

                await Task.Run(() => process.WaitForExit());

                if (process.ExitCode == 0)
                {
                    Logger.LogInfo(string.Format("{0}完成", type));
                }
                else
                {
                    Logger.LogError(string.Format("{0}失败，退出代码: {1}", type, process.ExitCode));
                }
            }
        }

        public void StopSync()
        {
            try
            {
                var process = currentProcess; // 创建本地引用
                if (process != null)
                {
                    try
                    {
                        if (!process.HasExited)
                        {
                            Logger.LogInfo("正在停止RClone进程...");
                            
                            try
                            {
                                // 首先尝试优雅地结束进程
                                process.CloseMainWindow();
                                
                                // 给进程一些时间来清理
                                if (!process.WaitForExit(5000))  // 等待5秒
                                {
                                    Logger.LogInfo("RClone进程未响应，强制终止...");
                                    process.Kill();  // 强制终止
                                }
                            }
                            catch (InvalidOperationException)
                            {
                                // 进程可能已经结束，忽略这个错误
                            }
                            
                            Logger.LogInfo("RClone进程已停止");
                        }
                    }
                    catch (InvalidOperationException)
                    {
                        // 进程可能已经结束，忽略这个错误
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogError("停止RClone进程时发生错误", ex);
            }
            finally
            {
                currentProcess = null;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!isDisposed)
            {
                if (disposing)
                {
                    StopSync();
                    if (currentProcess != null)
                    {
                        currentProcess.Dispose();
                        currentProcess = null;
                    }
                }
                isDisposed = true;
            }
        }
    }
} 